package com.itdct.onflow.core.aspect

import cn.hutool.core.collection.ConcurrentHashSet
import cn.hutool.core.lang.UUID
import com.alibaba.fastjson2.toJSONString
import com.baomidou.mybatisplus.extension.plugins.pagination.Page
import com.itdct.onflow.core.annotation.ApiParam
import com.itdct.onflow.core.config.OnflowConfig
import com.itdct.onflow.core.constant.ReturnCode
import com.itdct.onflow.core.context.BaseContext
import com.itdct.onflow.core.context.ContextHolder
import com.itdct.onflow.core.dto.BaseLoginDTO
import com.itdct.onflow.core.exception.CommonException
import com.itdct.onflow.core.extend.logInfo
import com.itdct.onflow.core.qo.BasePageQo
import com.itdct.onflow.core.qo.BaseQo
import com.itdct.onflow.core.util.NetworkUtil
import com.itdct.onflow.core.util.StringUtil
import com.itdct.onflow.core.vo.RespVo
import io.swagger.v3.oas.annotations.tags.Tag
import org.aspectj.lang.JoinPoint
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.AfterThrowing
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Pointcut
import org.aspectj.lang.reflect.MethodSignature
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import java.lang.reflect.Method

/**
 * Created by DCT-Portable on 2021/4/12 23:57.
 * Usage:
 */
abstract class BaseControllerAspect<AccountEntity : Any, LoginDTO : BaseLoginDTO<AccountEntity>>(
    open val onflowConfig: OnflowConfig
) {
    val duplicateSet = ConcurrentHashSet<String>()

    init {
        logInfo("初始化BaseControllerAspect")
    }

    @Pointcut("execution(public * com.itdct..*.controller..*(..))")
    fun pointCut() {
    }

    @Around("pointCut()")
    @Throws(Throwable::class)
    fun around(joinPoint: ProceedingJoinPoint): Any? {
        val args = joinPoint.args
        var arg = Any()
        if (args.isNotEmpty()) {
            arg = args[0] ?: throw CommonException(ReturnCode.LACK_PARAM, "请求对象不存在")
        }

        // INFO: DCT: 2023/9/2 初始化整个接口的上下文
        val context = initContext(arg, joinPoint)

        // INFO: Zhouwx: 2025/9/5 初始化token信息
        initToken(context)

        if (onflowConfig.checkPermission) {
            checkApiEnable(context)

            if (onflowConfig.checkToken) {
                checkToken(context)
            }

            if (onflowConfig.checkApiPermission) {
                checkApiPermission(context)
            }
        }

        // INFO: Zhouwx: 2024/10/25 初始化请求信息
        initForm(arg, context)

        // INFO: DCT: 2023/9/3 执行controller中的方法，如果有Cache会直接返回
        val resultObj = processArgs(context, joinPoint)

        // INFO: DCT: 2023/9/3 处理返回参数，以及处理Cache
        resultObj?.also {
            handleResponse(context, it)
        }

        // INFO: DCT: 2023/9/3 当全部完成
        onFinish(context)
        return resultObj
    }

    open fun initToken(context: BaseContext<AccountEntity, BaseLoginDTO<AccountEntity>>) {
        // INFO: Zhouwx: 2025/8/25 初始化用户token信息，请自行实现，也可参考system模块
    }

    open fun checkApiEnable(context: BaseContext<AccountEntity, BaseLoginDTO<AccountEntity>>) {
        // INFO: Zhouwx: 2025/4/27 检查api接口是否生效，自行实现
    }

    open fun checkApiPermission(context: BaseContext<AccountEntity, BaseLoginDTO<AccountEntity>>) {
        // INFO: Zhouwx: 2025/4/27 自行实现
    }

    open fun checkToken(context: BaseContext<AccountEntity, BaseLoginDTO<AccountEntity>>) {
        // INFO: Zhouwx: 2025/2/24 自行实现
    }

    @AfterThrowing(pointcut = "pointCut()", throwing = "e")
    open fun doAfterThrowing(joinPoint: JoinPoint?, e: Throwable?) {
//        log.info("doAfterThrowing", e);
    }

    @Throws(Throwable::class)
    open fun processArgs(context: BaseContext<AccountEntity, BaseLoginDTO<AccountEntity>>, joinPoint: ProceedingJoinPoint): Any? {
        // INFO: DCT-Portable: 2021/4/25 下一步进入Service的切面，Service返回MessageModel，而Controller直接返回了转换好的JSON
        return joinPoint.proceed(joinPoint.args)
    }

    open fun handleResponse(context: BaseContext<AccountEntity, BaseLoginDTO<AccountEntity>>, resultObj: Any) {
        // INFO: DCT-Portable: 2021/4/25 目前的result已经是Json类型的了
        if (resultObj is RespVo<*>) {
            context.respVo = resultObj
            // INFO: Zhouwx: 2025/8/13 在正确值范围内返回code 200，否则全部返回500
            if (resultObj.code in 200..onflowConfig.successCodeMaxValue) {
                context.response?.also {
                    it.status = 200
                }
            } else {
                context.response?.also {
                    it.status = 500
                }
            }
        } else {
            logInfo("handleResponse not RespVo type type is " + resultObj.javaClass.name)
        }
        if (context.apiParamDTO.important) {
            // INFO: Zhouwx: 2025/9/23 只有重要接口才需要转json
            context.responseBody = resultObj.toJSONString()
        }
        context.response?.let {
            val headMap = HashMap<String, String>()
            for (headerName in it.headerNames) {
                headMap[headerName] = it.getHeader(headerName)
            }
            context.responseHeadMap.putAll(headMap)
        }
    }

    open fun onFinish(context: BaseContext<AccountEntity, BaseLoginDTO<AccountEntity>>) {
        val finishTime = System.currentTimeMillis()
        context.finishTime = finishTime
        val totalUseTime = finishTime - context.receiveTime
        context.totalUseTime = totalUseTime

        beforeRemoveContext(context)
        ContextHolder.removeContext()
    }

    open fun beforeRemoveContext(context: BaseContext<AccountEntity, BaseLoginDTO<AccountEntity>>) {

    }

    open fun initContext(arg: Any, joinPoint: ProceedingJoinPoint): BaseContext<AccountEntity, BaseLoginDTO<AccountEntity>> {
        val context = setContext()

        context.receiveTime = System.currentTimeMillis()
        // INFO: DCT-Portable: 2021/4/18 请求类型判断
        val attributes = RequestContextHolder.getRequestAttributes() as ServletRequestAttributes
        val request = attributes.request
        val response = attributes.response
        val requestURI = request.requestURI
        val headMap = HashMap<String, String>()
        for (headerName in request.headerNames) {
            val headerValue = request.getHeader(headerName)
            headMap[headerName] = headerValue
            if (headerName.equals("content-type", true)) {
                context.requestContentType = headerValue.lowercase()
            }
        }
        context.requestHeadMap.putAll(headMap)
        val ipAddress = NetworkUtil.getIpAddress(request)
        val lastIndexOfUri = requestURI.lastIndexOf("/")
        val requestInterface = requestURI.substring(lastIndexOfUri + 1)
        context.requestApi = requestInterface
        context.request = request
        context.response = response
        context.requestUri = requestURI
        context.ipAddress = ipAddress
        context.requestModuleName = requestURI.split("/".toRegex()).toTypedArray()[1]
        val requestUuid = UUID.fastUUID().toString(true)
        context.requestUuid = requestUuid

        // 获取目标类
        val targetClass = joinPoint.target.javaClass
        context.controllerClass = targetClass

        val methodSignature = joinPoint.signature as MethodSignature
        // 获取方法名
        val methodName = methodSignature.name
        context.methodName = methodName
        // 获取方法参数类型
        val parameterTypes = methodSignature.parameterTypes
        // 获取目标方法
        val method: Method = targetClass.getMethod(methodName, *parameterTypes)
        context.method = method

        val apiAnnotation = targetClass.getAnnotation(Tag::class.java)
        if (apiAnnotation != null) {
            context.controllerModuleName = apiAnnotation.name
        }

        val apiParam = method.getAnnotation(ApiParam::class.java)
        if (apiParam != null) {
            val apiParamDTO = context.apiParamDTO
            apiParamDTO.important = apiParam.important
            apiParamDTO.onlyMyself = apiParam.onlyMyself
            apiParamDTO.onlyDepartment = apiParam.onlyDepartment
        }

        if (context.apiParamDTO.important) {
            // INFO: Zhouwx: 2025/9/23 只有重要接口才需要转json
            if (context.requestContentType.contains("multipart/form-data")) {
                context.requestBody = "{\"file\":true}"
            } else {
                context.requestBody = arg.toJSONString()
            }
        }

        afterInitContent(context)
        return context
    }

    open fun afterInitContent(context: BaseContext<AccountEntity, BaseLoginDTO<AccountEntity>>) {

    }

    open fun setContext(): BaseContext<AccountEntity, BaseLoginDTO<AccountEntity>> {
        val context = BaseContext<AccountEntity, BaseLoginDTO<AccountEntity>>()
        ContextHolder.setContext(context)
        return context
    }

    open fun initForm(arg: Any, context: BaseContext<AccountEntity, BaseLoginDTO<AccountEntity>>) {
        if (arg is BaseQo) {
            context.qo = arg
            checkDuplicate(context.qo!!)

            if (arg is BasePageQo) {
                handleOrderBy(context, arg)
                arg.keyword = StringUtil.likeFuzzy(arg.keyword)
                if (arg.pageNum != null && arg.pageSize != null) {
                    val page: Page<*> = Page<Any>(arg.pageNum!!, arg.pageSize!!.toLong())
                    context.page = page
                }
            }
        } else {
            context.qo = arg
        }
    }

    private fun checkDuplicate(form: Any) {
        if (!onflowConfig.checkDuplicate) {
            return
        }

        if (!(form is BaseQo)) {
            return
        }

        val rt = form.rt
        rt?.also {
            if (!it.contains("-")) {
                throw CommonException(ReturnCode.VALIDATE_ERROR, "参数校验失败")
            }
            val split = it.split("-")
            val timestampString = split[0]
            var timestamp = 0L

            try {
                timestamp = timestampString.toLong()
            } catch (e: Exception) {
                throw CommonException(ReturnCode.VALIDATE_ERROR, "参数校验失败")
            }
            if (System.currentTimeMillis() < timestamp) {
                // INFO: Zhouwx: 2024/10/25 当前时间小于请求时间，这肯定是有问题的
                throw CommonException(ReturnCode.VALIDATE_ERROR, "参数校验失败")
            } else if (System.currentTimeMillis() - timestamp > 120000) {
                // INFO: Zhouwx: 2024/10/25 收到了10分钟之前的请求，这个肯定也不行
                throw CommonException(ReturnCode.VALIDATE_ERROR, "参数校验失败")
            }
            val uniqueKey = split[1]
            if (uniqueKey.length != 8) {
                throw CommonException(ReturnCode.VALIDATE_ERROR, "参数校验失败")
            }

            if (duplicateSet.contains(uniqueKey)) {
                throw CommonException(ReturnCode.VALIDATE_ERROR, "检测到重复请求")
            }
            duplicateSet.add(uniqueKey)

            if (duplicateSet.size > 1000000) {
                duplicateSet.clear()
            }

        } ?: throw CommonException(ReturnCode.LACK_PARAM, "缺少请求参数")
    }

    open fun handleOrderBy(context: BaseContext<AccountEntity, BaseLoginDTO<AccountEntity>>, arg: BasePageQo) {
        val sortOrder = arg.sortOrder
        var sortBy = arg.sortBy
        if (sortBy != null && sortOrder != null && sortBy.isNotEmpty() && sortOrder.isNotEmpty()) {
            var order = ""
            if (sortOrder == "+") {
                order = "asc"
            } else {
                order = "desc"
            }
            arg.orderBy = "$sortBy $order"
        } else {
            arg.orderBy = null
        }
    }

}